﻿using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using DependencyAnalysis.DataModel;
using theeg.DependencyAnalysis.CommonAddin.Models;
using theeg.GraphWpf.Arrangement;
using theeg.GraphWpf.Models;

namespace theeg.DependencyAnalysis.CommonAddin.Views
{
    public partial class ArchitectureGraph
    {
        private bool _graphInvalidated;
        private bool _initialized;
        private bool _hideMode;

        private readonly IList<IDescriptor> _nodesToHide = new List<IDescriptor>();
        private readonly IDictionary<IDescriptor, Brush> _originalBackgroundForNodesToHide = new Dictionary<IDescriptor, Brush>();

        public ICommand ChangeDescriptorCommand
        {
            get { return (ICommand)GetValue(ChangeDescriptorCommandProperty); }
            set { SetValue(ChangeDescriptorCommandProperty, value); }
        }

        public static readonly DependencyProperty ChangeDescriptorCommandProperty =
            DependencyProperty.Register("ChangeDescriptorCommand", typeof(ICommand), typeof(ArchitectureGraph), new PropertyMetadata());

        public ICommand HideNodesCommand
        {
            get { return (ICommand)GetValue(HideNodesCommandProperty); }
            set { SetValue(HideNodesCommandProperty, value); }
        }

        public static readonly DependencyProperty HideNodesCommandProperty =
            DependencyProperty.Register("HideNodesCommand", typeof(ICommand), typeof(ArchitectureGraph), new PropertyMetadata());

        public GraphParameter GraphContent
        {
            get { return (GraphParameter)GetValue(GraphContentProperty); }
            set { SetValue(GraphContentProperty, value); }
        }

        public static readonly DependencyProperty GraphContentProperty =
            DependencyProperty.Register("GraphContent", typeof(GraphParameter), typeof(ArchitectureGraph), new PropertyMetadata());

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            if (e.Property == GraphContentProperty)
            {
                if (!_initialized)
                {
                    _graphInvalidated = true;
                }
                else
                {
                    BuildGraph();
                }
            }
            base.OnPropertyChanged(e);
        }

        private void BuildGraph()
        {
            Sketch.Nodes.Clear();
            Sketch.Transitions.Clear();
            if (GraphContent != null)
            {
                foreach (var node in GraphContent.Nodes)
                {
                    Sketch.Nodes.Add(node);
                }
                foreach (var transition in GraphContent.Transitions)
                {
                    Sketch.Transitions.Add(transition);
                }
                Sketch.Arrangement = new Layered(Sketch, 20, 80, GraphContent.HorizontalLayout)
                {
                    RemoveTransitiveEdges = GraphContent.HideTransitiveEdges
                };
                Sketch.Arrange();
            }
            _graphInvalidated = false;
        }

        public ArchitectureGraph()
        {
            InitializeComponent();
            Sketch.Loaded += Sketch_Initialized;
        }

        private void Sketch_Initialized(object sender, System.EventArgs e)
        {
            if (_graphInvalidated)
            {
                BuildGraph();
            }
            _initialized = true;
        }

        public void ChangeDescriptor(IDescriptor descriptor)
        {
            var command = ChangeDescriptorCommand;
            if (command == null)
            {
                return;
            }
            var changeDescriptorParameter = new GraphParameter { Descriptor = descriptor };
            command.Execute(changeDescriptorParameter);
        }

        private void ClickGraphItem(object sender, RoutedEventArgs e)
        {
            var transition = e.OriginalSource as Transition;
            if (transition != null)
            {
                var transitionDescriptor = new TransitionDescriptor
                {
                    Source = transition.Source.DataObject as IDescriptor,
                    Target = transition.Target.DataObject as IDescriptor
                };
                ChangeDescriptor(transitionDescriptor);
            }
            e.Handled = true;
        }

        private void DoubleClickGraphItem(object sender, RoutedEventArgs e)
        {
            var node = e.OriginalSource as Node;
            if (node != null && (node.DataObject is IDescriptor))
            {
                var descriptor = (IDescriptor)node.DataObject;
                if (_hideMode)
                {
                    if (_nodesToHide.Contains(descriptor))
                    {
                        _nodesToHide.Remove(descriptor);
                        Brush originalBrush;
                        _originalBackgroundForNodesToHide.TryGetValue(descriptor, out originalBrush);
                        node.Background = originalBrush ?? new SolidColorBrush(Colors.Green);
                        if (originalBrush != null)
                        {
                            _originalBackgroundForNodesToHide.Remove(descriptor);
                        }
                    }
                    else
                    {
                        _nodesToHide.Add(descriptor);
                        _originalBackgroundForNodesToHide[descriptor] = node.Background;
                        node.Background = new SolidColorBrush(Colors.Crimson);
                    }
                }
                else
                {
                    ChangeDescriptor(node.DataObject as IDescriptor);
                }
                e.Handled = true;
            }
        }

        private void ToggleOptions(object sender, RoutedEventArgs e)
        {
            var visibility = OptionsPopup.Visibility;
            OptionsPopup.Visibility = visibility == Visibility.Collapsed ? Visibility.Visible : Visibility.Collapsed;
        }

        private void ToggleHideMode(object sender, RoutedEventArgs e)
        {
            _hideMode = HideButton.IsChecked ?? false;
            if (!_hideMode)
            {
                var command = HideNodesCommand;
                if (command == null)
                {
                    return;
                }
                command.Execute(_nodesToHide.ToArray());
            }
            _nodesToHide.Clear();
            _originalBackgroundForNodesToHide.Clear();
        }
    }
}
